package com.usoft.thrift.monitor;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 服务功能运行监控信息
 * Created by xinxingegeya on 16/3/3.
 */
public class ServiceMonitorInfo {
    private Object serviceImpl;
    //服务调用总次数
    private AtomicLong serviceCount = new AtomicLong(0L);
    //业务方法列表
    private final List<BizMethodInfo> bizMethodInfoList = new ArrayList<>();
    //业务方法调用信息
    private final ConcurrentHashMap<String, BizMethodInvokeInfo> bizMethodInvokeInfoMap = new ConcurrentHashMap<>();

    public ServiceMonitorInfo(Object serviceImpl) {
        this.serviceImpl = serviceImpl;
        this.initServiceInfo();
    }

    public long getServiceCount() {
        return this.serviceCount.longValue();
    }

    private void incrementServiceCount() {
        this.serviceCount.incrementAndGet();
    }

    public List<BizMethodInfo> getBizMethodInfoList() {
        return this.bizMethodInfoList;
    }

    public void addBizMethodInfo(BizMethodInfo info) {
        this.bizMethodInfoList.add(info);
        this.initBizMethodInvokeInfo(info.getName());
    }

    public ConcurrentHashMap<String, BizMethodInvokeInfo> getBizMethodInvokeInfoMap() {
        return this.bizMethodInvokeInfoMap;
    }

    private void initBizMethodInvokeInfo(String method) {
        BizMethodInvokeInfo bizMethodInvokeInfo = new BizMethodInvokeInfo();
        bizMethodInvokeInfo.setFailureCount(0);
        bizMethodInvokeInfo.setSuccessAverageTime(0);
        bizMethodInvokeInfo.setSuccessCount(0);
        bizMethodInvokeInfo.setSuccessMaxTime(0);
        bizMethodInvokeInfo.setSuccessMinTime(0);
        bizMethodInvokeInfo.setTotalCount(0);
        this.bizMethodInvokeInfoMap.put(method, bizMethodInvokeInfo);
    }

    /**
     * 业务方法调用信息更新
     * 
     * @param method
     *        方法名
     * @param isSuccess
     *        是否成功
     * @param invokeTime
     *        方法调用时间，如果失败，不统计时间
     */
    public void updateBizMethodInvokeInfo(String method, boolean isSuccess,
            long invokeTime) {
        this.incrementServiceCount();
        BizMethodInvokeInfo bizMethodInvokeInfo = this.bizMethodInvokeInfoMap
            .get(method);
        //以下是一组原子性的操作
        synchronized (bizMethodInvokeInfo) {
            if (isSuccess) {
                if (invokeTime < bizMethodInvokeInfo.getSuccessMinTime()
                    || bizMethodInvokeInfo.getSuccessMinTime() == 0) {
                    bizMethodInvokeInfo.setSuccessMinTime(invokeTime);
                }

                if (invokeTime > bizMethodInvokeInfo.getSuccessMaxTime()
                    || bizMethodInvokeInfo.getSuccessMaxTime() == 0) {
                    bizMethodInvokeInfo.setSuccessMaxTime(invokeTime);
                }
                long totalTime = bizMethodInvokeInfo.getSuccessAverageTime()
                    * bizMethodInvokeInfo.getSuccessCount();
                bizMethodInvokeInfo
                    .setSuccessAverageTime((invokeTime + totalTime)
                        / (bizMethodInvokeInfo.getSuccessCount() + 1));
                bizMethodInvokeInfo
                    .setSuccessCount(bizMethodInvokeInfo.getSuccessCount() + 1);
            } else {
                bizMethodInvokeInfo
                    .setFailureCount(bizMethodInvokeInfo.getFailureCount() + 1);
            }
            bizMethodInvokeInfo
                .setTotalCount(bizMethodInvokeInfo.getTotalCount() + 1);
        }
    }

    /**
     * 注册服务方法信息，主要是是业务方法，便于后面监控
     */
    private void initServiceInfo() {
        Class<?>[] interfaces = serviceImpl.getClass().getInterfaces();
        for (Class<?> clazz : interfaces) {
            String clazzSimpleName = clazz.getSimpleName();
            if (!clazzSimpleName.equals("Iface")) {
                continue;
            }
            Method[] methods = clazz.getMethods();
            for (Method m : methods) {
                BizMethodInfo bizMethodInfo = new BizMethodInfo();
                bizMethodInfo.setName(m.getName());
                bizMethodInfo.setArgNum(m.getParameterCount());
                List<String> argsType = new ArrayList<>();
                for (Class<?> argType : m.getParameterTypes()) {
                    argsType.add(argType.getName());
                }
                bizMethodInfo.setArgsType(argsType);
                this.addBizMethodInfo(bizMethodInfo);
            }
        }
    }

    public boolean isBizMethod(String method) {
        return this.bizMethodInvokeInfoMap.containsKey(method);
    }

    @Override
    public String toString() {
        return "ServiceMonitorInfo{" + "serviceImpl=" + serviceImpl
            + ", serviceCount=" + serviceCount + ", bizMethodInfoList="
            + bizMethodInfoList + ", bizMethodInvokeInfoMap="
            + bizMethodInvokeInfoMap + '}';
    }
}
